import { _decorator, resources, Asset } from "cc";
import Singleton from "../Base/Singleton";
import { ApiMsgEnum, IModel, strdecode, strencode } from "../Common";
import { binaryDecode, binaryEncode } from "../Common/Binary";
// 定义回调函数接口
interface IItem {
    cb: Function;
    ctx: unknown;
}
// callApi的返回值类型
interface ICallApiRet<T>{
    success:boolean
    error?:Error
    res?:T
}
/**
 * 使用发布订阅模式，跟eventManager很像
 */
export class NetWorkManager extends Singleton {
    static get Instance() {
        return super.GetInstance<NetWorkManager>();
    }
    //是否连接
    isConnected = false
    port = 9876
    ws:WebSocket
    // 存储消息监听器的映射表
    private map: Map<ApiMsgEnum, Array<IItem>> = new Map();

    //链接后端
    connect(){
        //在调用connect方法的时候，外界需要知道什么时候完成，所以把他们封装成一个proimse  
        //只有真正的异常（如网络错误）才会使用 reject
        return new Promise((resolve,reject)=>{
            if(this.isConnected){
                resolve(true)
                return
            }         
            // 创建 WebSocket 连接
            this.ws  =new WebSocket(`ws://localhost:${this.port}`)
            this.ws.binaryType = "arraybuffer"
            // 连接成功时的回调
            this.ws.onopen=()=>{
                this.isConnected=true
                resolve(true)
            }
            // 连接关闭时的回调
            this.ws.onclose=()=>{
                this.isConnected=false
                reject(false)
            }
            // 发生错误时的回调
            this.ws.onerror=(e)=>{
                this.isConnected=false
                console.log(e)
                reject(false+"连接失败，服务端 yarn dev了没")
            }
            //跟eventManager的emit方法很像
            //ws.onmessage 是 WebSocket 对象的一个事件处理器，用于处理接收到的消息。这里用于接收服务器返回的消息
            // 它等同于 ws.on('message', ...) 的写法。
            this.ws.onmessage=(e)=>{
                try {
                    const json=binaryDecode(e.data)
                    //把 WebSocket 收到的二进制数据转换成一个 Uint8Array 类型的字节数组。
                    // const ta = new Uint8Array(e.data)
                    // //把字节数组解码还原成字符串。
                    // const str =strdecode(ta)
                    // // 解析接收到的 JSON 数据
                    // // const json = JSON.parse(e.data)
                    // const json = JSON.parse(str)
                    //这个name和data是前后端约定好的
                    const{name,data}=json
                    // 查找map，如果存在对应的消息监听器，则调用所有回调函数
                    if (this.map.has(name)) {
                        this.map.get(name).forEach(({ cb, ctx }) => {
                            //call 是 JavaScript 中函数对象的一个方法，用于调用函数并指定函数执行时的 this 值和参数。
                            cb.call(ctx, data);
                            //等同于：ctx.cb(data)

                        });
                      } 
                } catch (e) {
                    console.log(e)
                }
                
          }
      })
    }
    // 调用api，返回类型为ICallApiRet 给callApi和返回值ICallApiRet添加了泛型约束
    callApi<T extends keyof IModel['api']>(name: T, data:IModel['api'][T]['req']):Promise<ICallApiRet<IModel['api'][T]['res']>> {
        // 返回一个Promise对象
        return new Promise((resolve) => {
            try {
                // 设置5秒超时定时器
                const timer = setTimeout(() => {
                    // 超时后返回失败结果
                    resolve({ success: false, error: new Error("请求超时") });
                    // 取消消息监听
                    this.unlistenMsg(name as any, cb, this);
                }, 5000);
    
                // 定义回调函数
                const cb = (res) => {
                    // 收到响应后返回服务端传入的消息
                    resolve(res);
                    // 清除超时定时器
                    clearTimeout(timer);
                    // 取消消息监听
                    this.unlistenMsg(name as any, cb, this);
                };
    
                // 监听服务器返回的消息，将name,cb绑定到map
                this.listenMsg(name as any, cb, this);
                // 在这里发送消息到服务器
                this.sendMsg(name as any, data);
            } catch (error) {
                // 发生错误时返回失败结果
                resolve({ success: false, error });
            }
        });
    }
    // 发送消息 添加了泛型约束
    async sendMsg<T extends keyof IModel['msg']>(name:T,data:IModel['msg'][T]){
        //结构化数据
        const msg = {
            name,
            data,
        }
        //模拟网络延迟
        // await new Promise((rs)=>setTimeout(rs,2000))
        //发送消息，json.stringify将对象转换为字符串
        // this.ws.send(JSON.stringify(msg))

        //
        const da = binaryEncode(name,data)

        // msg 转换成 JSON 字符串(Unicode编码)
        // const str = JSON.stringify(msg)
        // //把 JSON 字符串编码成 UTF-8 字节数组
        // const ta = strencode(str)
        // //创建一个和字节数组长度相同的二进制缓冲区（ArrayBuffer），用于存放最终要发送的数据。
        // const ab = new ArrayBuffer(ta.length)
        // //用 DataView 包装这个缓冲区，方便以字节为单位写入数据。
        // const da = new DataView(ab)
        // //遍历字节数组，把每个字节写入到 DataView 对应的位置。
        // for (let index = 0; index < ta.length; index++) {
        //     da.setUint8(index,ta[index])           
        // }
        //通过 WebSocket 发送最终的二进制数据（ArrayBuffer）到服务器。
        this.ws.send(da.buffer)
    }
    
    // 监听特定类型的消息 添加了泛型约束
    //相当于eventmanager.instance.on（name,cb,this）
    listenMsg<T extends keyof IModel['msg']>(name:T,cb:(args:IModel['msg'][T])=>void,ctx:unknown){
        // 如果已存在该类型的监听器，则添加到数组中
        if (this.map.has(name)) {
            this.map.get(name).push({ cb, ctx });
        } else {
            // 如果不存在，则创建新的监听器数组
            this.map.set(name, [{ cb, ctx }]);
        }
    }

    //解绑类似off方法 添加了泛型约束
    unlistenMsg<T extends keyof IModel['msg']>(name:T,cb:(args:IModel['msg'][T])=>void,ctx:unknown){
        if (this.map.has(name)) {
            const index = this.map.get(name).findIndex((i) => cb === i.cb && i.ctx === ctx);
            /**
             * if (index > -1) {  // 如果找到了要删除的元素（index 不是 -1）
             * this.map.get(name).splice(index, 1);  // 从数组中删除这个元素
             * }
             */
            index > -1 && this.map.get(name).splice(index, 1);
        }
    }
}
